package com.gr.experimental.model.impl.distr.operations;

import com.gr.experimental.model.impl.distr.Context;

public class FunctionCall implements  Expression {

	private Function function;
	private Expression[] arguments;
	
	public FunctionCall(Function function, Expression ... arguments) {
		this.function = function;
		this.arguments = arguments;
		
		Class<?>[] parameterTypes = function.getParameterTypes();
		if (arguments.length != parameterTypes.length) {
			throw new IllegalArgumentException("Bad argument number");
		}
		for (int i=0; i<arguments.length; i++) {
			if ( !parameterTypes[i].isAssignableFrom(arguments[i].getValueType())) {
				throw new IllegalArgumentException("Incompatible argument no " + i + "." +
						" Class " + parameterTypes[i] + " not assignable from " 
						+ arguments[i].getValueType());
			}
		}
	}
	
	@Override
	public Object getValue(Context context) throws CalculationException {
		
		Object[] parameters = new Object[arguments.length];
		for (int i=0; i < arguments.length; i++) {
			Object value = arguments[i].getValue(context);
			parameters[i] = value;
		}
		
		try {
			return function.invoke(context, parameters);
		} catch (FunctionCallException e) {
			throw new CalculationException("Error calculating expression", e);
		}
	}

	@Override
	public Class<?> getValueType() {
		return function.getReturnType();
	}

}
